/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package Planificacion.Algoritmo;

/**
 *
 * @author Tatooine
 */
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;

public class GRASP {
    private int NumAutos;
    private int NumMotos;
    private int VelAutos;
    private int VelMotos;
    private int CapMotos;
    private int CapAutos;
    private int CostoKmAuto;
    private int CostoKmMoto;
    private int CostoProducto;
    private Coordenada Origen; //Origen es tienda de donde sale el pedido, podría haber más de una...
    private ArrayList<Producto> ArrayProductos= new ArrayList();
    //private ArrayList<Turno> ArrayTurnoVehiculo= new ArrayList<>();
    private int NumPedidos;
    private int CostoRetraso;//Costo por hora
    private double alfa1=0.12;
    private double alfa2=0.15;
    
    //CONSTRUCTOR
    public GRASP(int NumAutos, int NumMotos, int VelAutos, int VelMotos, int CapMotos,int CapAutos, int CostoKmAuto, int CostoKmMoto, int CostoProducto, Coordenada Origen,ArrayList<Producto> ArrayProductos,int NumPedidos,int CostoRetraso){
        this.NumAutos=NumAutos;
        this.NumMotos=NumMotos;
        this.VelAutos=VelAutos;
        this.VelMotos=VelMotos;
        this.CapAutos=CapAutos;
        this.CapMotos=CapMotos;
        this.CostoKmAuto=CostoKmAuto;
        this.CostoKmMoto=CostoKmMoto;
        this.CostoProducto=CostoProducto;
        this.Origen=Origen;
        this.ArrayProductos=CopiarArrayList(ArrayProductos);
        this.NumPedidos=NumPedidos;
        this.CostoRetraso=CostoRetraso;
    }
    //--------------------------------------------------------------------------------------------------------------
    public static ArrayList<Producto> CopiarArrayList(ArrayList<Producto> ArrayProductos){
        ArrayList<Producto> listaAux=new ArrayList();
        for (Producto producto : ArrayProductos) {
            listaAux.add(producto);
        }
        return listaAux;
    }
    //---------------------------------------------------------------------------------------------------------------
    public void MostrarContenidoVariables(){
        System.out.println("Número Autos: "+ NumAutos);
        System.out.println("Número Motos: "+ NumMotos);
        System.out.println("Velocidad Autos: "+ VelAutos);
        System.out.println("Velocidad Motos: "+ VelMotos);
        System.out.println("Capacidad Autos: "+ CapAutos);
        System.out.println("Capacidad Motos: "+ CapMotos);
        System.out.println("CostoKmAuto: "+ CostoKmAuto);
        System.out.println("CostoKmMoto: "+ CostoKmMoto);
        System.out.println("CostoProducto: "+ CostoProducto);
        System.out.println("Alfa1: "+ alfa1);
        System.out.println("Alfa2: "+ alfa2);
        System.out.println("PRODUCTOS: ");
        for (Producto producto : ArrayProductos) {
            System.out.println("IdPedido: "+ producto.getIdPedido()+ " Costo"+producto.getCosto());
        }
    }
    //----------------------------------------------------------------------------------------------------------------
    public void MostrarDatosVehiculos(ArrayList<Vehículo> listaVehiculos){
        for (Vehículo vehículo : listaVehiculos) {
            System.out.println("IdVehiculo: "+vehículo.getIdVehiculo()+" Capacidad: "+vehículo.getCapacidad()+" Tipo: "+vehículo.getTipo()+" Velocidad: "+vehículo.getVelocidad());
        }
    }
    //----------------------------------------------------------------------------------------------------------------
    public ArrayList<Vehículo> InicializaArrayVehiculos(int NumMotos,int NumAutos,int VelMoto,int VelAuto,int CapacMoto,int CapacAuto,int CostoKmMoto,int CostoKmAuto,int NumProductos,Coordenada Origen){
         ArrayList<Vehículo> listaVehiculos=new ArrayList();
         int TotalVehiculos=NumMotos+NumAutos;
            for (int i = 0; i < TotalVehiculos; i++) {
                if(i<NumMotos) listaVehiculos.add(new Vehículo(i+1,0, VelMoto, CapacMoto,CostoKmMoto,NumProductos,Origen));
                else listaVehiculos.add(new Vehículo(i+1,1, VelAuto, CapacAuto,CostoKmAuto,NumProductos,Origen));
            }
         return listaVehiculos;
    }
    //----------------------------------------------------------------------------------------------------------------
    public static ArrayList<Integer> RCLAsignacion(ArrayList<Vehículo> lista,double alfa){
            //Como lo que queremos es maximizar ganancia, entonces ordenamos de mayor a menor FO el array de vehiculos
            ArrayList<Vehículo> listaAux=new ArrayList();
            
            for (Vehículo vehículo : lista) {//copia a un nuevo array, no recuerdo si en caso ordenara afectara cuando salga de este metodo
                //System.out.println("Id Vehiculo: "+vehículo.getIdVehiculo()+" Capacidad: "+vehículo.getCapacidad()+" NumProdContiene"+vehículo.getNumProdContiene());
                listaAux.add(vehículo);
            }
            
            double beta;
            double tau;
            Collections.sort(listaAux,Collections.reverseOrder());//ordeno descendentemente la lista de vehiculos
            
            beta=listaAux.get(0).getFO();//mejor, 
            tau=listaAux.get(listaAux.size()-1).getFO();//peor, 
            //double rangoFinal=beta-alfa*(beta-tau);
            double rangoFinal=beta-alfa*(beta-tau);
            //RCL=beta<= lista<=beta+alfa*(tau-beta)
            //mi rango estará entre beta y rangoFinal=beta+alfa*(tau-beta);
            //en el array que arrojaré solo guardaré los id de los vehiculos q cumplen la condición
            ArrayList<Integer> RCL=new ArrayList();
            RCL.clear();
            for (Vehículo vehiculo : listaAux) {
                if(vehiculo.getFO()<=beta && vehiculo.getFO()>=rangoFinal) RCL.add(vehiculo.getIdVehiculo());
            }
            return RCL;
    }
    //----------------------------------------------------------------------------------------------------------------
    public static ArrayList<Integer> RCLAsignacionTurno(ArrayList<Turno> lista, double alfa,int capVeh){
            //Como lo que queremos es maximizar ganancia, entonces ordenamos de mayor a menor FO el array de vehiculos
            ArrayList<Turno> listaAux=new ArrayList();
            
            for (Turno turno : lista) {//copia a un nuevo array, no recuerdo si en caso ordenara afectara cuando salga de este metodo
                listaAux.add(turno);
            }
            
            double beta;
            double tau;
            Collections.sort(listaAux,Collections.reverseOrder());//ordeno descendentemente la lista de turnos
            
            beta=listaAux.get(0).getFO();//mejor, 
            tau=listaAux.get(listaAux.size()-1).getFO();//peor, 
            //double rangoFinal=beta-alfa*(beta-tau);
            double rangoFinal=beta-alfa*(beta-tau);
            //RCL=beta<= lista<=beta+alfa*(tau-beta)
            //mi rango estará entre beta y rangoFinal=beta+alfa*(tau-beta);
            //en el array que arrojaré solo guardaré los id de los vehiculos q cumplen la condición
            ArrayList<Integer> RCL=new ArrayList();
            RCL.clear();
            for (Turno turno : listaAux) {
                ArrayList<Producto> ArrAuxProd=turno.getProductos();
                if((turno.getFO()<=beta && turno.getFO()>=rangoFinal) && (ArrAuxProd.size()<capVeh) ) RCL.add(turno.getNumTurno());
            }
            return RCL;
    }
    //----------------------------------------------------------------------------------------------------------------
    public static int Aleatorio(ArrayList<Integer> lista){
            int tamanho=lista.size();//tamanho de lista entre la cual se elegirá uno
            int numSorteado;
            int IdVehiculoElegido_TurnoElegido;
            Random r=new Random();
            numSorteado=r.nextInt(tamanho);
            IdVehiculoElegido_TurnoElegido=lista.get(numSorteado);
            return IdVehiculoElegido_TurnoElegido;
    }
    //----------------------------------------------------------------------------------------------------------------
    public ArrayList<Integer> InicializaArrayRetrasos(int NumPedidos){
        ArrayList<Integer> listaAux=new ArrayList();
        int auxtiempo=80;
        int auxresta=-5;
        for (int i = 0; i < NumPedidos; i++) {
            auxtiempo+=auxresta;
            listaAux.add(auxtiempo);
        }
        return listaAux;
    }
    //----------------------------------------------------------------------------------------------------------------
    public boolean VerificaDestinoExiste(ArrayList<Coordenada> ListaDestinos,Coordenada Lugar){
        for (Coordenada coordenada : ListaDestinos) {
            if((coordenada.getX()==Lugar.getX()) && (coordenada.getY()==Lugar.getY())) return true;
        }
        return false;
    }
    //----------------------------------------------------------------------------------------------------------------
    public void EjecutaGRASP(){
        ArrayList<Vehículo> ArrayVehiculos=InicializaArrayVehiculos(NumMotos,NumAutos,VelMotos,VelAutos,CapMotos,CapAutos,CostoKmMoto,CostoKmAuto,ArrayProductos.size(),Origen);//ArrayProd.size me da Número de Productos
        ArrayList<Integer> ArrayTiempoQuedaPedidos=InicializaArrayRetrasos(NumPedidos); //DE MOMENTO EN UN INICIO 60 minutos todos los pedidos
        //Recordar que Array de turnos ya está inicializado en constructor de vehiculo
        //MostrarDatosVehiculos(ArrayVehiculos);
        Vehículo va=ArrayVehiculos.get(0);
        System.out.println(va.getTipo()+" "+va.getCapacidad());
        int IdVehiculoElegido;
        int Distancia=0;
        for (Producto Prod  : ArrayProductos) {//Recorreré todos los productos para asignarlos a vehículo y luego darle un turno
                //System.out.println(Prod.getIdPedido());
                //Calculo distancia entre origen y destino
                int IdPedidoProd=Prod.getIdPedido();
                int CostoProd=Prod.getCosto();
                Coordenada Destino=Prod.getDestino();
                int TiempoRest=Prod.getTiempoRestante();
                RespuestaVoraz PruebaVoraz=new RespuestaVoraz();
                ArrayList<Coordenada> c=new ArrayList();
                c.add(Origen);
                c.add(Prod.getDestino());
                PruebaVoraz=PruebaVoraz.Voraz(false, c);
                
                Distancia=PruebaVoraz.distancia.get(0);//como solo obtengo una distancia, es 0 el primer indice. El que necesito
                
                for (Vehículo vehiculo : ArrayVehiculos) {//Se recorre cada vehículo y se ejecuta FO=Ganancia/(Distancia*costoKMVehiculo)
                    int NumProdVehiculo=vehiculo.getNumProdContiene();
                    int DistAcumVehiculo=vehiculo.getDistanciaAcumulada();//Distancia Acumulada hasta ese momento recorrida
                    //En la FO se le agrega a la DistAcumVehiculo, la Distancia encontrada en el punto anterior(origen,destino)
                    //Y a NumProdVehiculo le agrego el producto solo para evaluar,sumo 1 ya que el vehiculo lo podria llevar pero será evaluado
                    double FO_1=(1.0)/((DistAcumVehiculo+Distancia)*vehiculo.getCostoXKm());
                    
                    vehiculo.setFO(FO_1);//Actualizo FO en objetivo
                    
                }//Al acabar el FOR el ArrayAuxFO tiene todas las FO para todos los vehiculos
                //Ahora se arma la lista de candidatos
                ArrayList<Integer> ArrayAux;//Se guardará los IdVehiculos de lista RCL
                ArrayAux=RCLAsignacion(ArrayVehiculos,alfa1);//obtengo un array de enteros que son los Id de los vehículos
                //Ahora se hará un aleatorio de esta lista para elegir el vehiculo al cual se le asignará el producto
                IdVehiculoElegido=Aleatorio(ArrayAux);
                //El indice del vehiculo es el id-1, asi que seteamos ese indice 
                Vehículo v1=ArrayVehiculos.get(IdVehiculoElegido-1);
                v1.setDistanciaAcumulada(v1.getDistanciaAcumulada()+Distancia);
                v1.setNumProdContiene(v1.getNumProdContiene()+1);
                ArrayList<Turno> ArrayTurnoVehiculo=v1.getTurnos();
                int tipoVeh=v1.getTipo();
                //SEGUNDA RELAJACIÓN
                //FO=(Ganancia-Tretraso*KcostoRetrasoHora) NECESITARÍA EN FUTURO UN ARRAY DE PEDIDOS CON SU RETRASO
                //ahora que ya se agrego un producto mas y se le sumo la distancia a la distancia acumulada al vehiculo
                //se procede a elegir en que turno irá ese producto. Recordar que son como máximo (NumProducto-1) turnos en el peor de los casos 
                int TiempoTotalQueda=ArrayTiempoQuedaPedidos.get(Prod.getIdPedido()-1);//obtengo el tiempo que queda al pedido
                Coordenada LugarDestino= Prod.getDestino();//Obtenfo el destino del pedido
                int NumTurnoElegido;
                int VelVeh=0;
                int capVeh=0;
                boolean insertaDestino=false;
                if(tipoVeh==0){
                    VelVeh=VelMotos;
                    capVeh=CapMotos;
                }//si 0 es moto
                else {
                    VelVeh=VelAutos;
                    capVeh=CapAutos;
                }//si 1 es auto
                RespuestaVoraz ObjVoraz=new RespuestaVoraz();//Obtendre distancias con este objeto
                for (Turno turno : ArrayTurnoVehiculo) {//recorrer todos los turnos de vehiculo elegido
                    //auxiliar para probar FO en cada turno
                    Turno AuxTurno=new Turno(turno);//Paso todo lo de turno a un Auxiliar de turno para probar valores
                    ArrayList<Coordenada> ArrDestinosTurno=new ArrayList();
                    ArrDestinosTurno=AuxTurno.getDestinos();
                    //antes de agregar el nuevo destino para probar debo verificar que el destino no se repita ya que no tiene sentido
                    if(VerificaDestinoExiste(ArrDestinosTurno,LugarDestino)==false){//si no existe destino lo agrego, sino no vale la pena
                        ArrDestinosTurno.add(LugarDestino);
                        insertaDestino=true;
                    } 
                    
                    ObjVoraz=ObjVoraz.Voraz(true, ArrDestinosTurno);//le paso el arreglo de destinos, obtendre distancia total....LUEGO VERIFICAR
                    int DistanciaTotalAux=ObjVoraz.distancia.get(ObjVoraz.distancia.size()-1);
                    //hallar tiempo transcurrido en turno hasta el momento, luego eso sumarle al TIEMPO ACUMULADO. Eso me dará el TIEMPO QUE QUEDA REALMENTE
                    double TiempoTranscurrTurno=(DistanciaTotalAux*1.0)/VelVeh;
                    double TiempoFO=(turno.getTiempoAcumulado()+TiempoTranscurrTurno)*1.0;
                    double FO_2=(TiempoTotalQueda-TiempoFO)*1.0/TiempoTranscurrTurno;
                    if(FO_2<0)FO_2=FO_2*-100;
                    turno.setFO(FO_2);
                    AuxTurno.setFO(FO_2);
                }
                    
                    int DistanciaTotalAnterior;
                    double TiempoTranscurrAux;
                    ArrayAux.clear();
                    ArrayList<Coordenada> ArrDestinosAux=new ArrayList();//guardare los destinos anteriores, hallare tiempo y a eso le sumare su acumulado para ponerlo en el actual
                    ArrayAux=RCLAsignacionTurno(ArrayTurnoVehiculo,alfa2,capVeh);//obtengo un array de enteros que son numeros de turno
                    NumTurnoElegido=Aleatorio(ArrayAux);
                    Turno t1=ArrayTurnoVehiculo.get(NumTurnoElegido);
                    if(t1.getNumTurno()!=0){
                        Turno tanterior=ArrayTurnoVehiculo.get(NumTurnoElegido-1);
                        ArrDestinosAux=tanterior.getDestinos();
                        ObjVoraz=ObjVoraz.Voraz(true, ArrDestinosAux);
                        DistanciaTotalAnterior=ObjVoraz.distancia.get(ObjVoraz.distancia.size()-1);
                        TiempoTranscurrAux=(DistanciaTotalAnterior*1.0)/VelVeh;
                        double pruebad=tanterior.getTiempoAcumulado()+TiempoTranscurrAux;
                        t1.setTiempoAcumulado(tanterior.getTiempoAcumulado()+TiempoTranscurrAux);//seteo tiempo acumulado para ese turno
                    }
                    if(insertaDestino)t1.setDestinos(LugarDestino);
                    t1.setProductos(Prod);
                   
                    for (Turno turno : ArrayTurnoVehiculo) {
                        if(turno.getNumTurno()!=0){
                            int NumTurnoAux=turno.getNumTurno();
                            
                            Turno turnoAux=ArrayTurnoVehiculo.get(NumTurnoAux-1);
                            ArrDestinosAux=turnoAux.getDestinos();
                            double TiempoAcumuladoAnterior=turno.getTiempoAcumulado();
                            ObjVoraz=ObjVoraz.Voraz(true, ArrDestinosAux);
                            DistanciaTotalAnterior=ObjVoraz.distancia.get(ObjVoraz.distancia.size()-1);
                            TiempoTranscurrAux=(DistanciaTotalAnterior*1.0)/VelVeh;
                            turno.setTiempoAcumulado(TiempoAcumuladoAnterior+TiempoTranscurrAux);
                        }
                    }
                    
            }
//        for (Turno turno : ArrayTurnoVehiculo) {
//                        System.out.println(turno.getFO());
//            }
        
        
        for (Vehículo vehiculo : ArrayVehiculos) {
            System.out.println("ID VEHICULO:"+vehiculo.getIdVehiculo()+" TIPO VEHICULO: "+vehiculo.getTipo()+" CAPACIDAD: "+vehiculo.getCapacidad());
                ArrayList<Turno> ArrTurnosAux =vehiculo.getTurnos();
                for (Turno turno : ArrTurnosAux) {
                    System.out.println("NUMERO DE TURNO :"+turno.getNumTurno());
                    ArrayList<Coordenada> arrcord=turno.getDestinos();
                    ArrayList<Producto> arrprod=turno.getProductos();
                    
                    for (Coordenada coordenada : arrcord) {
                        
                        System.out.print("("+coordenada.getX()+","+coordenada.getY()+")");
                    }
                    for (Producto produc : arrprod) {
                        
                        System.out.print(" IDPed("+produc.getIdPedido()+")");
                    }
                    System.out.println();
            }
                
        }
        
    }
}

